package com.coconet.gas.until;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;

/**
 * Created by solon on 2017/3/1. 远程调用工具
 */
public class SSHBase {
	String host;
	String user;
	private Session session;
	UserInfo ui;
	Integer port;

	final Logger logger = LoggerFactory.getLogger(SSHBase.class);

	public SSHBase(String host, String user, String passwd, Integer port) {
		this.host = host;
		this.user = user;
		this.ui = new MyUserInfo(passwd);
		this.port = port;
	}

	public void connect() {
		try {
			JSch jsch = new JSch();
			this.setSession(jsch.getSession(this.user, this.host, port));
			this.getSession().setUserInfo(this.ui);
			this.getSession().connect();
			logger.info("connect to {}@{} success.", this.user, this.host);
		} catch (JSchException e) {
			logger.error("connect to {} failed.", this.host, e);
		}
	}

	public JSONObject runCommand(String cmd) {
		Channel channel = null;
		StringBuffer cmdOutput = new StringBuffer();
		int cmdExitStatus = -1;
		boolean isSuccess = false;
		JSONObject result = new JSONObject();
		result.put("result", isSuccess);
		result.put("output", cmdOutput.toString());
		result.put("exitStatus", cmdExitStatus);
		try {
			channel = getSession().openChannel("exec");
			((ChannelExec) channel).setCommand(cmd);

			channel.setInputStream(null);

			((ChannelExec) channel).setErrStream(System.err);
			InputStream in = channel.getInputStream();
			channel.connect();
			byte[] tmp = new byte[1024];
			// logger.info("run \"{}\"", cmd);
			while (true) {
				while (in.available() > 0) {
					int i = in.read(tmp, 0, 1024);
					if (i < 0)
						break;
					cmdOutput.append(new String(tmp, 0, i));
				}
				if (channel.isClosed()) {
					if (in.available() > 0)
						continue;
					cmdExitStatus = channel.getExitStatus();
					break;
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					logger.error("run \"{}\" thread sleep error", cmd, e);
					return result;
				}
			}
			// logger.info("{} output is:{}", cmd, cmdOutput);
			// logger.info("{} ExitStatus is:{}", cmd, cmdExitStatus);
		} catch (JSchException e1) {
			logger.error("run \"{}\"", cmd, e1);
			return result;
		} catch (IOException e1) {
			logger.error("run \"{}\"", cmd, e1);
			return result;
		} finally {
			if (channel != null) {
				channel.disconnect();
			}
		}
		isSuccess = true;
		result.put("result", isSuccess);
		result.put("output", cmdOutput.toString());
		result.put("exitStatus", cmdExitStatus);
		return result;
	}

	public boolean scpToRemote(String srcFile, String dstFile) {
		Channel channel = null;
		try {
			FileInputStream fis = null;
			boolean ptimestamp = true;

			// exec 'scp -t rfile' remotely
			String command = "scp " + (ptimestamp ? "-p" : "") + " -t " + dstFile;
			channel = this.getSession().openChannel("exec");
			((ChannelExec) channel).setCommand(command);

			// get I/O streams for remote scp
			OutputStream out = channel.getOutputStream();
			InputStream in = channel.getInputStream();

			channel.connect();

			if (checkAck(in) != 0) {
				logger.error("scp {} check ack failed.", srcFile);
				return false;
			}

			File _lfile = new File(srcFile);

			if (ptimestamp) {
				command = "T " + (_lfile.lastModified() / 1000) + " 0";
				// The access time should be sent here,
				// but it is not accessible with JavaAPI ;-<
				command += (" " + (_lfile.lastModified() / 1000) + " 0\n");
				out.write(command.getBytes());
				out.flush();
				if (checkAck(in) != 0) {
					logger.error("scp {} check ack failed.", srcFile);
					return false;
				}
			}

			// send "C0644 filesize filename", where filename should not include '/'
			long filesize = _lfile.length();
			command = "C0644 " + filesize + " ";
			if (srcFile.lastIndexOf('/') > 0) {
				command += srcFile.substring(srcFile.lastIndexOf('/') + 1);
			} else {
				command += srcFile;
			}
			command += "\n";
			out.write(command.getBytes());
			out.flush();
			if (checkAck(in) != 0) {
				logger.error("scp {} check ack failed.", srcFile);
				return false;
			}

			// send a content of lfile
			fis = new FileInputStream(srcFile);
			byte[] buf = new byte[1024];
			while (true) {
				int len = fis.read(buf, 0, buf.length);
				if (len <= 0)
					break;
				out.write(buf, 0, len); // out.flush();
			}
			fis.close();
			// send '\0'
			buf[0] = 0;
			out.write(buf, 0, 1);
			out.flush();
			if (checkAck(in) != 0) {
				logger.error("scp {} check ack failed.", srcFile);
				return false;
			}
			out.close();
			channel.disconnect();
		} catch (JSchException e) {
			logger.error("scp {} failed.", srcFile, e);
			return false;
		} catch (IOException e) {
			logger.error("scp {} failed.", srcFile, e);
			return false;
		} finally {
			if (channel != null) {
				channel.disconnect();
			}
		}
		return true;
	}

	public void close() {
		if (this.getSession() != null) {
			this.getSession().disconnect();
		}
	}

	public class MyUserInfo implements UserInfo {
		String passwd;

		public MyUserInfo(String passwd) {
			this.passwd = passwd;
		}

		public String getPassword() {
			return passwd;
		}

		public boolean promptYesNo(String str) {
			return true;
		}

		public String getPassphrase() {
			return null;
		}

		public boolean promptPassphrase(String message) {
			return true;
		}

		public boolean promptPassword(String message) {
			return true;
		}

		public void showMessage(String message) {
			logger.info(message);
		}

	}

	int checkAck(InputStream in) throws IOException {
		int b = in.read();
		// b may be 0 for success,
		// 1 for error,
		// 2 for fatal error,
		// -1
		if (b == 0)
			return b;
		if (b == -1)
			return b;

		if (b == 1 || b == 2) {
			StringBuffer sb = new StringBuffer();
			int c;
			do {
				c = in.read();
				sb.append((char) c);
			} while (c != '\n');
			if (b == 1) { // error
				logger.error(sb.toString());
			}
			if (b == 2) { // fatal error
				logger.error(sb.toString());
			}
		}
		return b;
	}

	/** sftp 下载 */
	public InputStream down(String filePath) {
		ChannelSftp sftp=null;
		try {
			sftp = (ChannelSftp) getSession().openChannel("sftp");
			sftp.connect();
			logger.info("开始下载，sftp服务器路径：[" + filePath + "]目标服务器路径：[" + null + "]");
			InputStream in = sftp.get(filePath);
			int code= -1;
			while ((code=in.read())!=-1) {
				System.err.println(code);
			}
			logger.info("下载成功");
			return in;
		} catch (Exception e) {
			logger.error("下载失败", e);
			return null;
		} finally {
			sftp.disconnect();
		}
	}

	public Session getSession() {
		return session;
	}

	public void setSession(Session session) {
		this.session = session;
	}
}
